package com.code.translate.utils;

import com.alibaba.fastjson.JSON;
import com.code.translate.enums.Constants;
import com.code.translate.httpbuilder.ContentTypeBuilder;
import org.springframework.http.*;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

/**
 * @author ggh
 * @date 2022/7/8
 */
public class HttpUtils {
    private final static int connectTimeOut = 30000;
    private final static int readTimeOut = 30000;
    private final static String DEFAULT_CONTENT_TYPE = MediaType.APPLICATION_FORM_URLENCODED_VALUE;
    private final static String streamContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";

    private HttpUtils() {
    }

    /**
     * 发送http请求
     *
     * @param url
     * @param method
     * @param headers
     * @param params
     * @throws IOException
     */
    public static String req(String url, HttpMethod method, Map<String, String> headers, Map<String, Object> params) throws IOException {
        //如果是get请求，拼接url
        if (!CollectionUtils.isEmpty(params) && HttpMethod.GET.equals(method)) {
            String queryParam = buildQueryParam(params);
            url = spliceUrl(url, queryParam);
        }

        URL httpUrl = new URL(url);
        URLConnection rulConnection = httpUrl.openConnection();
        HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;

        if (HttpMethod.POST.equals(method)) {
            httpUrlConnection.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
            httpUrlConnection.setRequestMethod(method.name());
            httpUrlConnection.setDoOutput(true);
        }
        httpUrlConnection.setUseCaches(false);

        //填充请求头
        fillDefaultHeader(httpUrlConnection);

        ContentTypeBuilder builder = null;
        if (!CollectionUtils.isEmpty(headers)) {
            String contentType = headers == null || headers.get(HttpHeaders.CONTENT_TYPE) == null ? DEFAULT_CONTENT_TYPE : headers.get(HttpHeaders.CONTENT_TYPE);
            builder = ContentTypeBuilder.getBuilder(contentType);
            try {
                builder.headerbuilder(httpUrlConnection, headers);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            builder = ContentTypeBuilder.getBuilder(null);
        }

        httpUrlConnection.setConnectTimeout(connectTimeOut);
        httpUrlConnection.setReadTimeout(readTimeOut);
        httpUrlConnection.connect();

        //如果是post请求，填充数据
        if (HttpMethod.POST.equals(method) && builder != null) {
            OutputStream os = httpUrlConnection.getOutputStream();
            try {
                builder.bodybuilder(os, params);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        String resp;
        if (httpUrlConnection.getResponseCode() == 200) {
            InputStream inputStream = httpUrlConnection.getInputStream();
            String charset = parseResponseCharset(httpUrlConnection);

            //使用响应编码解析响应流
            resp = IOUtils.readInputStreamWithResponse(inputStream, charset);
            httpUrlConnection.disconnect();
        } else {
            resp = IOUtils.readInputStream(httpUrlConnection.getErrorStream());
        }

        return resp;
    }

    /**
     * 发送http请求(用于处理用于处理流请求)
     *
     * @throws IOException
     */
    public static <T> ResponseEntity<T> reqEntity(RequestEntity<T> requestEntity, boolean output) throws IOException {
        //如果是get请求，拼接url
        String url = requestEntity.getUrl().toString();

        URL httpUrl = new URL(url);
        URLConnection rulConnection = httpUrl.openConnection();
        HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;

        if (HttpMethod.POST.equals(requestEntity.getMethod())) {
            httpUrlConnection.setRequestMethod(requestEntity.getMethod().name());
            httpUrlConnection.setDoOutput(true);
        }
        httpUrlConnection.setUseCaches(false);

        //填充请求头
        fillDefaultHeader(httpUrlConnection);
        if (requestEntity.getHeaders() != null) {
            for (Map.Entry<String, List<String>> entry : requestEntity.getHeaders().entrySet()) {
                httpUrlConnection.setRequestProperty(entry.getKey(), entry.getValue().get(0));
            }
        }

        httpUrlConnection.setConnectTimeout(connectTimeOut);
        httpUrlConnection.setReadTimeout(readTimeOut);
        httpUrlConnection.connect();

        //如果是post请求，填充数据
        if (HttpMethod.POST.equals(requestEntity.getMethod())) {
            OutputStream os = httpUrlConnection.getOutputStream();

            String contentType = requestEntity.getHeaders() == null || requestEntity.getHeaders().get(HttpHeaders.CONTENT_TYPE) == null ?
                    MediaType.APPLICATION_FORM_URLENCODED_VALUE : requestEntity.getHeaders().get(HttpHeaders.CONTENT_TYPE).get(0);

            if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
                String json = JSON.toJSONString(requestEntity.getBody());
                os.write(json.getBytes());
            } else if (contentType.contains(MediaType.TEXT_PLAIN_VALUE)) {
                os.write(requestEntity.getBody().toString().getBytes());
            } else {
                os.write(buildQueryParamWithEntity(requestEntity.getBody()).getBytes());
            }
        }
        //封装response
        ResponseEntity responseEntity = buildResponseEntity(httpUrlConnection, output);

        httpUrlConnection.disconnect();
        return responseEntity;
    }

    /**
     * 发送http请求(用于处理用于处理流请求)
     *
     * @throws IOException
     */
    public static <T> ResponseEntity<T> reqEntity(RequestEntity<T> requestEntity) throws IOException {
        return reqEntity(requestEntity, true);
    }

    private static <T> ResponseEntity<T> buildResponseEntity(HttpURLConnection httpURLConnection, boolean output) throws IOException {
        HttpStatus httpStatus = HttpStatus.valueOf(httpURLConnection.getResponseCode());
        ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.status(httpStatus);

        boolean isDownload = false;
        for (Map.Entry<String, List<String>> entry : httpURLConnection.getHeaderFields().entrySet()) {
            if (entry.getKey() != null) {
                responseBuilder.header(entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]));
            }
            if (streamContentType.equals(entry.getKey())) {
                isDownload = true;
            }
        }

        if (!output) {
            return responseBuilder.build();
        }

        ResponseEntity responseEntity = null;

        if (httpURLConnection.getResponseCode() == 200) {
            if (isDownload) {
                responseEntity = responseBuilder.body(httpURLConnection.getInputStream());
            } else {
                InputStream inputStream = httpURLConnection.getInputStream();
                String charset = parseResponseCharset(httpURLConnection);

                //使用响应编码解析响应流
                String resp = IOUtils.readInputStreamWithResponse(inputStream, charset);

                responseEntity = responseBuilder.body(resp);
            }
        } else {
            responseEntity = responseBuilder.body(IOUtils.readInputStream(httpURLConnection.getErrorStream()));
        }

        return responseEntity;
    }

    /**
     * 填充请求参数
     *
     * @return
     */
    private static <T> String buildQueryParamWithEntity(T body) {
        StringBuilder param = new StringBuilder();

        Map<String, Object> bodys = (Map<String, Object>) body;
        for (Map.Entry<String, Object> entry : bodys.entrySet()) {
            param.append(entry.getKey() + "=" + entry.getValue() + "&");
        }
        param.deleteCharAt(param.length() - 1);

        return param.toString();
    }

    /**
     * 填充请求参数
     *
     * @param params
     * @return
     */
    private static String buildQueryParam(Map<String, Object> params) {
        StringBuilder param = new StringBuilder();
        if (!CollectionUtils.isEmpty(params)) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                param.append(entry.getKey() + "=" + entry.getValue().toString() + "&");
            }
            param.deleteCharAt(param.length() - 1);
        }
        return param.toString();
    }

    /**
     * 拼接url
     *
     * @param url        源请求字符串
     * @param queryParam 查询参数
     * @return
     */
    private static String spliceUrl(String url, String queryParam) {
        if (url.contains("?")) {
            url = url + "&" + queryParam;
        } else {
            url = url + "?" + queryParam;
        }
        return url;
    }

    /**
     * 解析响应体编码
     *
     * @param httpUrlConnection
     * @return
     */
    private static String parseResponseCharset(HttpURLConnection httpUrlConnection) {
        String responseContentType = httpUrlConnection.getHeaderField(HttpHeaders.CONTENT_TYPE);
        if (StringUtils.isEmpty(responseContentType)) {
            responseContentType = httpUrlConnection.getHeaderField("content-type");
        }

        if (StringUtils.isEmpty(responseContentType)) {
            return Charset.forName("UTF8").displayName();
        }

        String[] contentTypes = responseContentType.split(Constants.HEADER_SEPARATOR);
        if (contentTypes == null && contentTypes.length == 0) {
            return null;
        }
        for (String contentType : contentTypes) {
            if (contentType.contains("charset")) {
                return contentType.split("=")[1];
            }
        }

        return null;
    }

    /**
     * 填充默认请求头
     *
     * @param httpURLConnection
     */
    private static void fillDefaultHeader(HttpURLConnection httpURLConnection) {
        httpURLConnection.setRequestProperty(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36");
        httpURLConnection.setRequestProperty("cache-control", "no-cache");
    }
}
